package com.wbcs.system;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspWriter;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.wbcs.config.Config;
import com.wbcs.exception.MessageCollector;
import com.wbcs.exception.WbcsRuntimeException;
import com.wbcs.exception.WbcsRuntimeWarningException;
import com.wbcs.system.assistant.WbcsAssistant;
import com.wbcs.util.Consts;
import com.wbcs.util.Tools;

public class WbcsResponse
{
    private static Log log=LogFactory.getLog(WbcsResponse.class);
    
    private PrintWriter out;

    private JspWriter jspout;
    
    private StringBuffer outBuf;

    private ReportRequest rrequest;
    
    private MessageCollector messageCollector;

    private int statecode=Consts.STATECODE_SUCCESS;
    
    private List<Map<String,String>> lstOnloadMethods;
    
    private List<String> lstAfterDomOnloadMethods;
    
    private List<Map<String, String>> lstCallbackMethods;
    
    private List<String> lstUpdateReportGuids;
    
    private String dynamicRefreshComponentGuid;
    
    private String dynamicSlaveReportId;
    
    private HttpServletResponse response;
    
    private boolean hasInitOutput;
    
    public WbcsResponse(HttpServletResponse response)
    {
        this.response=response;
        messageCollector=new MessageCollector(this);
    }

    public MessageCollector getMessageCollector()
    {
        return messageCollector;
    }

    public void addOnloadMethod(String methodname,String methodparams,boolean isInsertFirst)
    {
        if(methodname==null||methodname.trim().equals("")) return;
        if(lstOnloadMethods==null) lstOnloadMethods=new ArrayList<Map<String,String>>();
        Map<String,String> mMethod=new HashMap<String,String>();
        if(methodparams!=null&&!methodparams.trim().equals(""))
        {
            
            methodparams=methodparams.trim();
            if(!methodparams.startsWith("{")||!methodparams.endsWith("}"))
            {
                methodparams="{"+methodparams+"}";
            }
        }
        mMethod.put(methodname,methodparams);
        if(isInsertFirst)
        {
            lstOnloadMethods.add(0,mMethod);
        }else
        {//加在后面调用
            lstOnloadMethods.add(mMethod);
        }
    }
    
    /**
     * 使用jquery的方式在DOM加载完成后执行js方法
     * @author hawkfly
     * @datetime 201909111005
     */
    public void addAfterDomOnloadMethod(String methodname,String methodparams){
    	if(methodname==null||methodname.trim().equals("")) return;
        if(lstAfterDomOnloadMethods==null) lstAfterDomOnloadMethods=new ArrayList<String>();
        if(methodparams!=null&&!methodparams.trim().equals(""))
        {
            
            methodparams=methodparams.trim();
            if(!methodparams.startsWith("{")||!methodparams.endsWith("}"))
            {
                methodparams="{"+methodparams+"}";
            }
        }
        
        StringBuilder resultMethod = new StringBuilder();
        resultMethod.append("$(function(){");
        resultMethod.append(methodname).append("(").append(methodparams).append(");");
        resultMethod.append("});");
        
        lstAfterDomOnloadMethods.add(resultMethod.toString());
    }
    
    /**
     * 增加回调函数调用
     * @param targetMethodName 代替换方法名
     * @param srcMethodName 执行回调方法名
     * @param params  执行回到方法参数
     * @author hawkfly
     * @datetime 201909111137
     */
    public void addCallbackMethod(String targetMethodName, String srcMethodName, String params)
    {
    	if(targetMethodName==null||targetMethodName.trim().equals("")) return;
    	if(srcMethodName==null||srcMethodName.trim().equals("")) return;
        if(lstCallbackMethods==null) lstCallbackMethods=new ArrayList<Map<String, String>>();
        
        Map<String, String> mcallback = new HashMap<String, String>();
        mcallback.put("targetMethodName", targetMethodName);
        mcallback.put("srcMethodName", srcMethodName);
        
        if(params!=null&&!params.trim().equals(""))
        {
            
        	params=params.trim();
            if(!params.startsWith("{")||!params.endsWith("}"))
            {
            	params="{"+params+"}";
            }
        }
        mcallback.put("params", params);
        
        lstCallbackMethods.add(mcallback);
    }

    public HttpServletResponse getResponse()
    {
        return response;
    }

    public void setResponse(HttpServletResponse response)
    {
        this.response=response;
    }

    public StringBuffer getOutBuf()
    {
        return outBuf;
    }

    public int getStatecode()
    {
        return statecode;
    }

    public void setStatecode(int statecode)
    {
        if(statecode!=Consts.STATECODE_NONREFRESHPAGE&&statecode!=Consts.STATECODE_FAILED&&statecode!=Consts.STATECODE_SUCCESS)
        {
            log.warn("设置的响应状态码"+statecode+"不支持，将用默认的成功状态码");
            statecode=Consts.STATECODE_SUCCESS;
        }
        this.statecode=statecode;
    }
    
    public void terminateResponse(int statecode)
    {
        setStatecode(statecode);
        throw new WbcsRuntimeWarningException();
    }
    
    public void addUpdateReportGuid(String reportguid)
    {
        if(reportguid==null||reportguid.trim().equals("")) return;
        if(this.lstUpdateReportGuids==null)
        {
            this.lstUpdateReportGuids=new ArrayList<String>();
        }else if(this.lstUpdateReportGuids.contains(reportguid))
        {
            return;
        }
        this.lstUpdateReportGuids.add(reportguid);
    }

    public ReportRequest getRRequest()
    {
        return rrequest;
    }

    public void setRRequest(ReportRequest rrequest)
    {
        this.rrequest=rrequest;
    }

    public void setJspout(JspWriter jspout)
    {
        this.jspout=jspout;
    }

    /**
     * 将onload调用方法转化为字符串
     * @return
     */
    public String invokeOnloadMethodsFirstTime()
    {
    	//replace callback
        replaceCallbackMethod();
    	
        if(this.lstOnloadMethods==null||this.lstOnloadMethods.size()==0) return "";
        StringBuffer resultBuf=new StringBuffer();
        String methodName;
        String methodParams;
        for(Map<String,String> mMethodTmp:this.lstOnloadMethods)
        {
            methodName=mMethodTmp.keySet().iterator().next();
            methodParams=mMethodTmp.get(methodName);
            resultBuf.append(methodName).append("(");
            if(methodParams!=null&&!methodParams.trim().equals(""))
            {
               resultBuf.append(methodParams);
            }
            resultBuf.append(");");
        }
        
        //support load after dom scene, add by hawkfly on 201909111023
        if(lstAfterDomOnloadMethods != null)
	    {
        	for (String afterDomMethod : lstAfterDomOnloadMethods) {
	        	resultBuf.append(afterDomMethod);
			}
	    }
        
        
        return resultBuf.toString();
    }
    
    private void replaceCallbackMethod() {
		// TODO Auto-generated method stub
    	if(lstCallbackMethods != null)
        {
        	for (Map<String, String> mcallback : lstCallbackMethods)
        	{
        		for(Map<String,String> mMethodTmp:this.lstOnloadMethods)
        		{
        			if(mMethodTmp.containsKey(mcallback.get("targetMethodName")))
        			{
        				String methodName=mMethodTmp.keySet().iterator().next();
        	            String methodParams=mMethodTmp.get(methodName);
        	            if(methodParams == null)continue;
        	            methodParams = methodParams
        	            				.replace("fixedGridcallback", mcallback.get("srcMethodName"))
        	            				.replace("callbackParams", mcallback.get("params"));
        	            mMethodTmp.put(methodName, methodParams);
        			}
        		}
			}
        }
	}

	public String assembleResultsInfo(Throwable t)
    {
        String load_error_mess=Config.getInstance().getResources().getString(rrequest,rrequest.getPagebean(),Consts.LOADERROR_MESS_KEY,false);
        if(load_error_mess==null||load_error_mess.trim().equals(""))
        {
            load_error_mess="<strong>System is busy,Please try later</strong>";
        }else
        {
            load_error_mess=load_error_mess.trim();
            load_error_mess=rrequest.getI18NStringValue(load_error_mess);
        }
        if(rrequest.getPagebean()==null)
        {
            log.error("没有取到"+rrequest.getStringAttribute("PAGEID","")+"对应的页面配置");
            return "没有取到"+rrequest.getStringAttribute("PAGEID","")+"对应的页面配置";
        }
        
        StringBuffer resultBuf=new StringBuffer();
        if(rrequest.getShowtype()==Consts.DISPLAY_ON_PAGE)
        {
            String pageurlspan="<span id=\""+rrequest.getPagebean().getId()+"_url_id\" style=\"display:none;\" value=\""
                    +Tools.htmlEncode(Tools.jsParamEncode(rrequest.getUrl()))+"\"";
            if(rrequest.getPagebean().isShouldProvideEncodePageUrl())
            {
                pageurlspan=pageurlspan+" encodevalue=\""+Tools.convertBetweenStringAndAscii(rrequest.getUrl(),true)+"\"";
            }
            String ancestorUrls=rrequest.getStringAttribute("ancestorPageUrls","");
            if(!ancestorUrls.equals(""))
            {
                pageurlspan=pageurlspan+" ancestorPageUrls=\""+ancestorUrls+"\"";
            }
            resultBuf.append(pageurlspan+"></span>");
        }
        if(!rrequest.isLoadedByAjax()||(rrequest.getShowtype()!=Consts.DISPLAY_ON_PAGE&&rrequest.getShowtype()!=Consts.DISPLAY_ON_PRINT))
        {
            if(t!=null)
            {
                if(t instanceof WbcsRuntimeWarningException)
                {
                   resultBuf.append(addWarnMessage(false,"<br>"));
                   resultBuf.append(addSuccessMessage(false,"<br>"));
                   resultBuf.append(addAlertMessage(false,null));
                }else
                {
                    resultBuf.append(addErrorMessage(load_error_mess,false,null));
                }
            }else
            {
                resultBuf.append(addWarnMessage(false,"<br>"));
                resultBuf.append(addAlertMessage(false,"<br>"));
                resultBuf.append(addSuccessMessage(false,"<br>"));
                resultBuf.append(addErrorMessage(null,false,null));
            }
        }else
        {
            int showtype = rrequest.getShowtype();
            String pageid=rrequest.getPagebean().getId();
            resultBuf.append("<RESULTS_INFO-").append(pageid).append(">").append("{");
            resultBuf.append("pageurl:\"").append(rrequest.getUrl()).append("\",");
            //if(isForwardAction) resultBuf.append("isForwardAction:\"").append(isForwardAction).append("\",");
            if(rrequest.getPagebean().isShouldProvideEncodePageUrl())
            {
                resultBuf.append("pageEncodeUrl:\"").append(Tools.convertBetweenStringAndAscii(rrequest.getUrl(),true)).append("\",");
            }
            if(dynamicRefreshComponentGuid!=null&&!dynamicRefreshComponentGuid.trim().equals(""))
            {
                resultBuf.append("dynamicRefreshComponentGuid:\"").append(dynamicRefreshComponentGuid).append("\",");
                if(dynamicSlaveReportId!=null&&!dynamicSlaveReportId.trim().equals(""))
                {
                    resultBuf.append("dynamicSlaveReportId:\"").append(dynamicSlaveReportId).append("\",");
                }
            }
            resultBuf.append("statecode:").append(this.statecode).append(",");
            if(t!=null)
            {
                if(t instanceof WbcsRuntimeWarningException)
                {
                    resultBuf.append(addWarnMessage(true,","));
                    resultBuf.append(addSuccessMessage(true,","));
                    resultBuf.append(addAlertMessage(true,","));
                }else
                {
                    resultBuf.append(addErrorMessage(load_error_mess,true,","));
                }
            }else
            {
                resultBuf.append(addWarnMessage(true,","));
                resultBuf.append(addAlertMessage(true,","));
                resultBuf.append(addSuccessMessage(true,","));
                resultBuf.append(addErrorMessage(null,true,","));
            }
            if(this.lstOnloadMethods!=null&&this.lstOnloadMethods.size()>0)
            {
                resultBuf.append("onloadMethods:[");
                String methodNameTmp;
                String methodParamsTmp;
                for(Map<String,String> mMethodsTmp:this.lstOnloadMethods)//important!自执行方法追加
                {
                    methodNameTmp=mMethodsTmp.keySet().iterator().next();
                    methodParamsTmp=mMethodsTmp.get(methodNameTmp);
                    resultBuf.append("{methodname:").append(methodNameTmp);
                    if(methodParamsTmp!=null&&!methodParamsTmp.trim().equals(""))
                    {//如果此方法有参数，参数的格式必须为：param1:"value1",param2:"value2"
                        resultBuf.append(",methodparams:").append(methodParamsTmp);
                    }
                    resultBuf.append("},");
                }
                if(resultBuf.charAt(resultBuf.length()-1)==',')
                {
                    resultBuf.deleteCharAt(resultBuf.length()-1);
                }
                resultBuf.append("],");
            }
            if(lstUpdateReportGuids!=null&&lstUpdateReportGuids.size()>0)
            {
                resultBuf.append("updateReportGuids:[");
                for(String rguidTmp:lstUpdateReportGuids)
                {
                    resultBuf.append("{value:\"").append(rguidTmp).append("\"},");
                }
                if(resultBuf.charAt(resultBuf.length()-1)==',')
                {
                    resultBuf.deleteCharAt(resultBuf.length()-1);
                }
                resultBuf.append("],");
            }
            if(resultBuf.charAt(resultBuf.length()-1)==',')
            {
                resultBuf.deleteCharAt(resultBuf.length()-1);
            }
            resultBuf.append("}").append("</RESULTS_INFO-").append(pageid).append(">");
        }
        return resultBuf.toString();
    }
    
    private String addAlertMessage(boolean jsPrompt,String suffixIfExist)
    {
        String resultMess="";
        if(jsPrompt)
        {
            resultMess=this.messageCollector.getJsAlertMessages("  ");
            if(resultMess!=null&&!resultMess.trim().equals(""))
            {
                resultMess="alertmess:\""+resultMess.trim()+"\"";
            }
        }else
        {
            resultMess=this.messageCollector.getJsAlertMessages("<br>");
        }
        resultMess=resultMess==null?"":resultMess.trim();
        if(!resultMess.equals("")&&suffixIfExist!=null) resultMess=resultMess+suffixIfExist;
        return resultMess;
    }
    
    private String addSuccessMessage(boolean jsPrompt,String suffixIfExist)
    {
        String resultMess="";
        if(jsPrompt)
        {
            resultMess=this.messageCollector.getJsSuccessMessages("  ");
            if(resultMess!=null&&!resultMess.trim().equals(""))
            {
                resultMess="successmess:\""+resultMess.trim()+"\"";
            }
        }else
        {
            resultMess=this.messageCollector.getJsSuccessMessages("<br>");
        }
        resultMess=resultMess==null?"":resultMess.trim();
        if(!resultMess.equals("")&&suffixIfExist!=null) resultMess=resultMess+suffixIfExist;
        return resultMess;
    }
    
    private String addWarnMessage(boolean jsPrompt,String suffixIfExist)
    {
        String resultMess="";
        if(jsPrompt)
        {
            resultMess=this.messageCollector.getJsWarnMessages("  ");
            if(resultMess!=null&&!resultMess.trim().equals(""))
            {
                resultMess="warnmess:\""+resultMess.trim()+"\"";
            }
        }else
        {
            resultMess=this.messageCollector.getJsWarnMessages("<br>");
        }
        resultMess=resultMess==null?"":resultMess.trim();
        if(!resultMess.equals("")&&suffixIfExist!=null) resultMess=resultMess+suffixIfExist;
        return resultMess;
    }
    
    private String addErrorMessage(String defaultvalue,boolean jsPrompt,String suffixIfExist)
    {
        String resultMess="";
        if(jsPrompt)
        {
            resultMess=this.messageCollector.getJsErrorMessages("  ");
            if(resultMess==null||resultMess.trim().equals("")) resultMess=defaultvalue;
            if(resultMess!=null&&!resultMess.trim().equals(""))
            {
                resultMess="errormess:\""+resultMess.trim()+"\"";
            }
        }else
        {
            resultMess=this.messageCollector.getJsErrorMessages("<br>");
        }
        resultMess=resultMess==null?"":resultMess.trim();
        if(!resultMess.equals("")&&suffixIfExist!=null) resultMess=resultMess+suffixIfExist;
        return resultMess;
    }

    public void setDynamicRefreshComponentGuid(String componentguid,String slaveReportId)
    {
        this.dynamicRefreshComponentGuid=componentguid;
        this.dynamicSlaveReportId=slaveReportId;
    }
    
    public boolean isOutputImmediately()
    {
        if(this.response==null) return false;
        return true;
    }
    
    public void initOutput(String attachFilename)
    {
        hasInitOutput=true;
        if(response!=null&&rrequest.getRequest()!=null)
        {//本次是直接输出到页面
            rrequest.getRequest().getSession();
            if(rrequest.getShowtype()==Consts.DISPLAY_ON_RICHEXCEL)
            {
                attachFilename=attachFilename==null?"":attachFilename.trim();
                response.setHeader("Content-disposition","attachment;filename="
                        +WbcsAssistant.getInstance().encodeAttachFilename(rrequest.getRequest(),attachFilename)+".xls");
            }else if(rrequest.getShowtype()==Consts.DISPLAY_ON_WORD)
            {
                attachFilename=attachFilename==null?"":attachFilename.trim();
                response.setHeader("Content-disposition","attachment;filename="
                        +WbcsAssistant.getInstance().encodeAttachFilename(rrequest.getRequest(),attachFilename)+".doc");
            }
            try
            {
                out=response.getWriter();
            }catch(IOException e)
            {
                throw new WbcsRuntimeException("初始化页面输出失败",e);
            }
            if(rrequest.getShowtype()!=Consts.DISPLAY_ON_PRINT)
            {
                out.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
                out.println("<meta http-equiv=\"Content-Type\" content=\"text/html; charset="+Config.encode+"\">");
            }
        }else
        {
            this.outBuf=new StringBuffer();
            outBuf.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
            outBuf.append("<meta http-equiv=\"Content-Type\" content=\"text/html; charset="+Config.encode+"\">");
        }
    }
    
    public void println(String content)
    {
        println(content,false);
    }
    
    public void println(String content,boolean overwrite)
    {
        if(!hasInitOutput) initOutput(null);
        if(content==null||content.trim().equals("")) return;
        if(rrequest.getShowtype()==Consts.DISPLAY_ON_RICHEXCEL||rrequest.getShowtype()==Consts.DISPLAY_ON_WORD
                ||rrequest.getShowtype()==Consts.DISPLAY_ON_PRINT)
        {
            content=WbcsAssistant.getInstance().replaceAllImgPathInExportDataFile(rrequest.getRequest(),content);
        }
        if(jspout!=null)
        {//当前是通过<wx:report/>显示报表，则从jsp中取out进行输出
            try
            {
                jspout.println(content);
            }catch(IOException e)
            {
                
                e.printStackTrace();
            }
        }else if(out!=null)
        {
        	if(overwrite){
        		response.resetBuffer();
        	}
            out.println(content);
        }else
        {
            if(overwrite) outBuf=new StringBuffer();
            outBuf.append(content);
        }
        
        //log.info(content);
        /*if(mReportsWithIncludePage!=null&&mReportsWithIncludePage.size()>0)
        {
            rrequest.getRequest().setAttribute("WX_REPORTREQUEST",rrequest);
            String starttagTmp=ReportAssistant.getInstance().getStartTagOfIncludeFilePlaceholder(rrequest.getPagebean());
            String endtagTmp=ReportAssistant.getInstance().getEndTagOfIncludeFilePlaceholder(rrequest.getPagebean());
            int idx=content.indexOf(starttagTmp);
            String reportidTmp;
            AbsReportType reportTypeObjTmp;
            *
             * 每个包含外部页面的格式为<tag>reportid</tag>，其中<tag>和</tag>分别是starttagTmp和endtagTmp
             *//*
            String dynTplPathTmp=null;
            while(true)
            {
                if(idx<0) break;
                out.println(content.substring(0,idx));
                content=content.substring(idx+starttagTmp.length());
                idx=content.indexOf(endtagTmp);
                if(idx<0) break;
                reportidTmp=content.substring(0,idx);
                if(reportidTmp==null||reportidTmp.trim().equals("")||!this.mReportsWithIncludePage.containsKey(reportidTmp))
                {
                    idx=content.indexOf(starttagTmp);
                    continue;
                }
                reportTypeObjTmp=mReportsWithIncludePage.get(reportidTmp);
                rrequest.getRequest().setAttribute("WX_COMPONENT_OBJ",reportTypeObjTmp);
                if(rrequest.getShowtype()==Consts.DISPLAY_ON_PAGE)
                {
                    dynTplPathTmp=reportTypeObjTmp.getReportBean().getDynTplPath();
                }else
                {
                    dynTplPathTmp=reportTypeObjTmp.getReportBean().getDynDataExportTplPath();
                }
                RequestDispatcher rd=rrequest.getRequest().getRequestDispatcher(dynTplPathTmp);
                rd.include(rrequest.getRequest(),response);

                content=content.substring(idx+endtagTmp.length());
                idx=content.indexOf(starttagTmp);
            }
        }*/
    }
    
    public void print(String content)
    {
        print(content,false);
    }
    
    public void print(String content,boolean overwrite)
    {
        if(!hasInitOutput) initOutput(null);
        if(content==null||content.trim().equals("")) return;
        if(rrequest.getShowtype()==Consts.DISPLAY_ON_RICHEXCEL||rrequest.getShowtype()==Consts.DISPLAY_ON_WORD
                ||rrequest.getShowtype()==Consts.DISPLAY_ON_PRINT)
        {
            content=WbcsAssistant.getInstance().replaceAllImgPathInExportDataFile(rrequest.getRequest(),content);
        }
        if(jspout!=null)
        {//当前是通过<wx:report/>显示报表，则从jsp中取out进行输出
            try
            {
                jspout.print(content);
            }catch(IOException e)
            {
                
                e.printStackTrace();
            }
        }else if(out!=null)
        {
            out.print(content);
        }else
        {
            if(overwrite) outBuf=new StringBuffer();
            outBuf.append(content);
        }
    }
    
    public void sendRedirect(String url)
    {
        if(url==null||url.trim().equals("")) return;
        if(!rrequest.isLoadedByAjax())
        {
            try
            {
                this.response.sendRedirect(url);
            }catch(IOException e)
            {
                e.printStackTrace();
            }
        }else
        {
            this.addOnloadMethod("wx_sendRedirect","{url:\""+url+"\"}",false);
        }
        throw new WbcsRuntimeWarningException();
    }
}
